﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Mogre;
using MogreNewt;
using System.IO;


using System.Runtime.InteropServices;
using System.Threading;
using Win32;
using System.Xml.Serialization;

namespace MogreMicroscope
{
    public enum CollisionType
    {
        Box,
        Capsule,
        ChamferCylinder,
        Cone,
        Cylinder,
        Ellipsoid,
        Compound
    }

    public enum PrimitiveType
    {
        Node,
        BoxCol,
        CapsuleCol,
        ChamferCylinderCol,
        ConeCol,
        CylinderCol,
        EllipsoidCol,
        CompoundCol,
        ConvexHullCol,
        TreeCol,
        Undefined
    }

    public class ConvexCollisionItem
    {
        Vector3 _size;

        public Vector3 Size
        {
            get { return _size; }
            set { _size = value; }
        }
        Vector3 _position;

        public Vector3 Position
        {
            get { return _position; }
            set { _position = value; }
        }
        Quaternion _orientation;

        public Quaternion Orientation
        {
            get { return _orientation; }
            set { _orientation = value; }
        }
        ConvexCollision _col;

        [XmlIgnore]
        public ConvexCollision Col
        {
            get { return _col; }
            set { _col = value; }
        }
        public ConvexCollisionItem()
        {
        }
        public ConvexCollisionItem(Vector3 size, Vector3 pos, Quaternion orient, ConvexCollision col)
        {
            _size = size;
            _position = pos;
            _orientation = orient;
            _col = col;
        }
    }

    public class Primitive
    {
        #region Properties
        Vector3 _scale;
        Vector3 _position;
        Quaternion _orientation;
        SceneNode _node = null;
        Body _body = null;
        int _index;
        private List<Primitive> _bodyCols = new List<Primitive>();
        private List<ConvexCollisionItem> _cols = new List<ConvexCollisionItem>();


        string _displayName;
        string _name;
        float _mass;
        PrimitiveType _primitiveClass;

        bool _serialising = false;

        [XmlIgnore]
        public bool Serialising
        {
            get { return _serialising; }
            set { 
                _serialising = value;
                foreach (Primitive item in _bodyCols)
                {
                    item.Serialising = value;
                }
            }
        }

        public Vector3 Scale
        {
            get { return _scale; }
            set { _scale = value; }
        }


        public Vector3 Position
        {
            get
            {
                if (_serialising == false)
                {
                    if (IsBody() == true)
                    {
                        try
                        {
                            _body.GetPositionOrientation(out _position, out _orientation);
                        }
                        catch { }
                    }
                }
                return _position;
            }
            set
            {
                _position = value;
                if (_serialising == false)
                {
                    if (IsBody() == true)
                    {
                        _body.SetPositionOrientation(_position, _orientation);
                    }
                    else if (IsNode() == true)
                    {
                        _node.Position = _position;
                    }
                }
            }
        }


        public Quaternion Orientation
        {
            get
            {
                if (_serialising == false)
                {
                    if (IsBody() == true)
                    {
                        _body.GetPositionOrientation(out _position, out _orientation);
                    }
                }
                return _orientation;
            }
            set
            {
                _orientation = value;
                if (_serialising == false)
                {
                    if (IsBody() == true)
                    {
                        _body.SetPositionOrientation(_position, _orientation);
                    }
                    else
                    {
                        _node.Orientation = _orientation;
                    }
                }
            }
        }

        public AxisAlignedBox BoundingBox
        {
            get
            {
                try
                {
                    if (_serialising == false)
                    {
                        if (IsBody() == true)
                        {
                            return _body.BoundingBox;
                        }
                        if (IsNode() == true)
                        {
                            return _node._getWorldAABB();
                        }
                    }
                }
                catch { }
                return new AxisAlignedBox();
            }
        }

        [XmlIgnore]
        public SceneNode Node
        {
            get { return _node; }
            set { _node = value; }
        }

        [XmlIgnore]
        public Body Body
        {
            get { return _body; }
            set { _body = value; }
        }

        public List<Primitive> BodyCols
        {
            get { return _bodyCols; }
            set { _bodyCols = value; }
        }

        public List<ConvexCollisionItem> Cols
        {
            get { return _cols; }
            set { _cols = value; }
        }

        public int Index
        {
            get { return _index; }
            set { _index = value; }
        }



        public string DisplayName
        {
            get { return _displayName; }
            set { _displayName = value; }
        }


        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }



        public float Mass
        {
            get { return _mass; }
            set { _mass = value; }
        }


        public PrimitiveType PrimitiveClass
        {
            get { return _primitiveClass; }
            set { _primitiveClass = value; }
        }



        #endregion Properties
        public Primitive()
        {
            _serialising = true;
        }
        public Primitive(int index, string name, SceneNode node, Vector3 pos, Quaternion orientation, Vector3 scale)
        {
            _index = index;
            _name = name;
            _position = pos;
            _orientation = orientation;
            _scale = scale;
            _node = node;
            _primitiveClass = PrimitiveType.Node;

        }



        public Primitive(int index, string name, Body body, CollisionType colType, Vector3 pos, Quaternion orientation, Vector3 scale)
        {
            _index = index;
            _name = _displayName = name;
            _position = pos;
            _orientation = orientation;
            _scale = scale;
            _body = body;
            _primitiveClass = ColTypeToPrimTypeConverter(colType);
        }

        public Primitive(int index, string name, PrimitiveType type)
        {
            _index = index;
            _name = _displayName = name;
            _position = Vector3.ZERO;
            _orientation = Quaternion.IDENTITY;
            _scale = Vector3.UNIT_SCALE;
            _primitiveClass = type;
        }

        protected PrimitiveType ColTypeToPrimTypeConverter(CollisionType col)
        {
            PrimitiveType ret = PrimitiveType.Undefined;
            try
            {
                ret = (PrimitiveType)Enum.Parse(typeof(PrimitiveType), col.ToString() + "Col");
            }
            catch
            {
            }

            return ret;
        }
        public CollisionType PrimTypeToColTypeConverter(PrimitiveType col)
        {
            CollisionType ret = CollisionType.Box;
            try
            {
                ret = (CollisionType)Enum.Parse(typeof(CollisionType), col.ToString().Replace("Col", ""));
            }
            catch
            {
            }

            return ret;
        }

        public bool IsNode()
        {
            return (_node != null);
        }

        public bool IsBody()
        {
            return (_body != null);
        }

        public Vector3 RefreshBodyWithScale(World world, SceneManager sceneMgr)
        {
            MogreNewt.ConvexCollision cp = null;
            Vector3 inertia;
            Vector3 offset;

            //_body.GetInvMass(out mass, out inertia);
            //_body.GetMassMatrix(out mass, out inertia);


            switch (_primitiveClass)
            {

                case PrimitiveType.CapsuleCol:
                    cp = new MogreNewt.CollisionPrimitives.Capsule(world, _scale.x, _scale.y, 0);
                    break;
                case PrimitiveType.ChamferCylinderCol:
                    cp = new MogreNewt.CollisionPrimitives.ChamferCylinder(world, _scale.x, _scale.y, 0);
                    break;
                case PrimitiveType.ConeCol:
                    cp = new MogreNewt.CollisionPrimitives.Cone(world, _scale.x, _scale.y, 0);
                    break;
                case PrimitiveType.CylinderCol:
                    cp = new MogreNewt.CollisionPrimitives.Cylinder(world, _scale.x, _scale.y, 0);
                    break;
                case PrimitiveType.EllipsoidCol:
                    cp = new MogreNewt.CollisionPrimitives.Ellipsoid(world, _scale, 0);
                    break;

                default:
                    cp = new MogreNewt.CollisionPrimitives.Box(world, _scale, 0);
                    break;
            }
            _body.Collision = cp;


            cp.CalculateInertialMatrix(out inertia, out offset);
            inertia = inertia * _mass;

            cp.Dispose();


            _body.SetMassMatrix(_mass, inertia);
            _body.UpdateNode();
            world.InvalidateCache();
            world.DebuggerInstance.DeInit();
            world.DebuggerInstance.Init(sceneMgr);
            return _scale;
        }



        public void RebuildBody(World world, SceneManager sceneMgr)
        {
            MogreNewt.ConvexCollision cp = null;
            Vector3 inertia;
            Vector3 offset;

            switch (_primitiveClass)
            {

                case PrimitiveType.CapsuleCol:
                    cp = new MogreNewt.CollisionPrimitives.Capsule(world, _scale.x, _scale.y, 0);
                    break;
                case PrimitiveType.ChamferCylinderCol:
                    cp = new MogreNewt.CollisionPrimitives.ChamferCylinder(world, _scale.x, _scale.y, 0);
                    break;
                case PrimitiveType.ConeCol:
                    cp = new MogreNewt.CollisionPrimitives.Cone(world, _scale.x, _scale.y, 0);
                    break;
                case PrimitiveType.CylinderCol:
                    cp = new MogreNewt.CollisionPrimitives.Cylinder(world, _scale.x, _scale.y, 0);
                    break;
                case PrimitiveType.EllipsoidCol:
                    cp = new MogreNewt.CollisionPrimitives.Ellipsoid(world, _scale, 0);
                    break;

                default:
                    cp = new MogreNewt.CollisionPrimitives.Box(world, _scale, 0);
                    break;
            }
            _body = new MogreNewt.Body(world, cp);
            _body.SetPositionOrientation(_position,_orientation);

            cp.CalculateInertialMatrix(out inertia, out offset);
            inertia = inertia * _mass;

            cp.Dispose();


            _body.SetMassMatrix(_mass, inertia);
            _body.UpdateNode();
            world.InvalidateCache();
            world.DebuggerInstance.DeInit();
            world.DebuggerInstance.Init(sceneMgr);
        }



        public void CreateConvexHull(World world, SceneNode node)
        {

            MogreNewt.ConvexCollision col = new MogreNewt.CollisionPrimitives.ConvexHull(world, node, 1.0f, 0);
            _body = new MogreNewt.Body(world, col);
            //col.Dispose();


            Vector3 inertia = Vector3.ZERO;
            Vector3 offset = Vector3.ZERO;

            inertia = Vector3.UNIT_SCALE;
            _mass = 100;
            inertia = inertia * _mass;
            _body.SetMassMatrix(_mass, inertia);
            _body.UserData = _name;

            _body.IsGravityEnabled = true;
            _position = node.Position;
            _orientation = node.Orientation;
            _body.SetPositionOrientation(_position, _orientation);
            _body.AttachNode(node);
        }

        public void CreateCompound(World world, ref Primitive[] cols, SceneNode node)
        {

            _node = node;
            List<MogreNewt.Collision> cp = new List<Collision>();
            int loop = 0;
            _position = node.Position;
            _orientation = node.Orientation;
            foreach (Primitive item in cols)
            {
                if (item.Body != null)
                {
                    if (item.Body.Collision.GetType() == typeof(MogreNewt.CollisionPrimitives.Box))
                    {
                        cp.Add(new MogreNewt.CollisionPrimitives.Box(world, item.BoundingBox.Size, item.Orientation, _position - item.Position, 0));
                        continue;
                    }
                    if (item.Body.Collision.GetType() == typeof(MogreNewt.CollisionPrimitives.Cylinder))
                    {
                        cp.Add(new MogreNewt.CollisionPrimitives.Cylinder(world, item.BoundingBox.HalfSize.x, item.BoundingBox.Size.y, item.Orientation, item.Position, 0));
                        continue;
                    }
                    if (item.Body.Collision.GetType() == typeof(MogreNewt.CollisionPrimitives.Capsule))
                    {
                         cp.Add(new MogreNewt.CollisionPrimitives.Capsule(world, item.BoundingBox.HalfSize.x, item.BoundingBox.Size.y, item.Orientation, item.Position, 0));
                        continue;
                    }
                    if (item.Body.Collision.GetType() == typeof(MogreNewt.CollisionPrimitives.ChamferCylinder))
                    {
                         cp.Add(new MogreNewt.CollisionPrimitives.ChamferCylinder(world, item.BoundingBox.HalfSize.x, item.BoundingBox.Size.y, item.Orientation, item.Position, 0));
                        continue;
                    }
                    if (item.Body.Collision.GetType() == typeof(MogreNewt.CollisionPrimitives.Cone))
                    {
                         cp.Add(new MogreNewt.CollisionPrimitives.Cone(world, item.BoundingBox.HalfSize.x, item.BoundingBox.Size.y, item.Orientation, item.Position, 0));
                        continue;
                    }
                    if (item.Body.Collision.GetType() == typeof(MogreNewt.CollisionPrimitives.Ellipsoid))
                    {
                         cp.Add(new MogreNewt.CollisionPrimitives.Ellipsoid(world, item.BoundingBox.Size, item.Orientation, item.Position, 0));
                        continue;
                    }
                    if (item.Body.Collision.GetType() == typeof(MogreNewt.CollisionPrimitives.Pyramid))
                    {
                         cp.Add(new MogreNewt.CollisionPrimitives.Pyramid(world, item.BoundingBox.Size, item.Orientation, item.Position, 0));
                        continue;
                    }
                }

            }

            MogreNewt.Collision[] cps = cp.ToArray();
            MogreNewt.Collision col = new MogreNewt.CollisionPrimitives.CompoundCollision(world, cps, 0);
            _body = new MogreNewt.Body(world, col);
            col.Dispose();


            Vector3 inertia = Vector3.ZERO;
            Vector3 offset = Vector3.ZERO;

            inertia = Vector3.UNIT_SCALE;
            _mass = 100;
            inertia = inertia * _mass;
            _body.SetMassMatrix(_mass, inertia);

            _body.UserData = _name;
            _body.IsGravityEnabled = true;

            _body.AttachNode(node);

        }

        public void CreateTree(World world, SceneNode node)
        {

            MogreNewt.Collision col = new MogreNewt.CollisionPrimitives.TreeCollision(world, node, false, 0, MogreNewt.CollisionPrimitives.FaceWinding.FW_REVERSE);
            _body = new MogreNewt.Body(world, col);
            col.Dispose();


            Vector3 inertia = Vector3.ZERO;
            Vector3 offset = Vector3.ZERO;
            inertia = Vector3.UNIT_SCALE;
            _mass = 100;
            inertia = inertia * _mass;
            _body.SetMassMatrix(_mass, inertia);
            _body.UserData = _name;
            _body.IsGravityEnabled = true;
            _position = node.Position;
            _orientation = node.Orientation;
            _body.SetPositionOrientation(_position, _orientation);

        }

        public void Dispose()
        {
            if (_body != null)
            {
                _body.Dispose();
                _body = null;
            }
            if (_node != null)
            {
                _node.Dispose();
                _node = null;
            }
        }
    }

}
